Skip to content

spec(system): DataMigrationFlagSchema gains columns_moved_at, the nullable datetime attesting that a migration's column move ran on this deployment - #17068

Merged
os-bill merged 5 commits into
mainfrom
claude/issue-16185-migration-flag-column-move-attest
Sep 9, 2026
Merged

spec(system): DataMigrationFlagSchema gains columns_moved_at, the nullable datetime attesting that a migration's column move ran on this deployment#17068
os-bill merged 5 commits into
mainfrom
claude/issue-16185-migration-flag-column-move-attest

Conversation

@os-bill

@os-bill os-bill commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator

Fixes #16185
Parent card: #15989 — the ruling that created this one. ⛔ NOT delivered here and deliberately not a Part of relation: that keyword means this PR delivers part of the named card, and this PR delivers none of #15989 (nothing under packages/drivers/** changes; #15989's arm-1 work stays blocked on this landing). The earlier Part of #15989 line was written on the dispatching seat's instruction and was factually wrong; corrected by that seat, and the correction is recorded on #16185.

Clause-②: yes

DataMigrationFlagSchema gains one member — columns_moved_at, a nullable optional datetime — and the sys_migration platform object gains the matching column. That is the whole behavioural change. Nothing writes the member, nothing reads it, and no other member of the schema is changed, re-typed or re-ordered.

Mechanism A was ruled on #15989 (comment 5556979386) and is not re-opened here:

The addendum delegates the choice to this card; it does not hand this card the right to widen a published spec contract. ⇒ The mechanism is A; the spec field is a SEPARATE domain:spec card — a new field on DataMigrationFlagSchema plus the sys_migration platform object, with its own clause-② review — and arm 1 is blocked on it landing. ⛔ Do not smuggle a spec contract field into a driver card.

Naming: why columns_moved_at

The card left the name to this seat and set one criterion — it has to read correctly, as a normal expected state rather than an error, on a row where it is null on a deployment that has run the backfill but not the column move. Triage added a second: it has to land inside the naming and describe() style of the three members it joins.

Both were checked against the members as they stand on this base, not from memory:

member shape name form
verified_at z.string().datetime().nullable().optional() past participle + _at
applied_at z.string().datetime().nullable().optional() past participle + _at
deviation_observed_at z.string().datetime().nullable().optional() noun + past participle + _at
columns_moved_at (new) z.string().datetime().nullable().optional() noun + past participle + _at

columns_moved_at is the fourth of that set by shape, and takes the third one's exact name form — the two-part SUBJECT_PASTPARTICIPLE_at spelling that already exists in this object for the member whose subject is not the migration itself. Read as null it says "the columns have not been moved here", which is a statement about the world, not a failure: it is the true and expected reading on every deployment that has applied and verified the backfill and not yet run the move.

Two alternatives were considered and rejected on this tree:

  • columns_migrated_at — collides semantically with applied_at, whose own describe() on this base is "When the backfill last ran in apply mode (writes enabled)". The single fact the new member exists to separate is backfill ran from columns moved; a name built on "migrated" re-merges the two words the member is there to pull apart.
  • A bare moved_at — in style, but the subject is load-bearing. verified_at and applied_at take the migration as their implicit subject; the thing moved here is the columns, not the migration, which is why the sibling with a different subject (deviation_observed_at) also spells its subject out.

The word "moved" is the ruling's and the card's own vocabulary for this step ("my media columns have been moved", "the column move and the driver's arm flip must be one act"). Because "moved" could be misread as relocated or renamed, the describe() says what the step actually does — retypes the columns and rewrites the values they hold into the new encoding — rather than leaning on the verb.

Absence is the contract, and it is pinned as one

The card's first binding constraint, verbatim: "Optional + nullable is therefore not a style choice — it is the contract." packages/spec/src/system/data-migration-flag-column-move.pin.test.ts holds it:

  • a row that omits the member parses green, and 'columns_moved_at' in parsed is false after the parse — no default of any kind is injected, including a null one;
  • an explicit null parses green and survives as null;
  • the member is a DECLARED member and not an accepted-then-stripped stray: the schema is not .strict(), so the instrument is that the value survives the parse, with an undeclared columns_moved_at_typo sibling asserted as the control that shows the same parse DOES strip a stray;
  • a non-timestamp ('', 'yes', '2026-09-09') is rejected, so nothing that is not a timestamp can pose as this evidence;
  • and the type half: DataMigrationFlag (z.input of the schema) accepts both an object that omits the member and one that says columns_moved_at: null. Those two declarations are compiled — measured with tsc --listFiles, the pin file is absent from the package's plain tsc --noEmit program and present in the tsconfig.test.json program that pnpm --filter @objectstack/spec typecheck runs through check:test-typecheck, so a required-or-non-nullable member would redden typecheck, not sit as dead text.

The ONE arbiter is unchanged — the pin the card requires

isDataMigrationFlagVerified has zero diff lines in this PR; both changed source files are pure insertions (git diff --stat: 14 insertions, 0 deletions across the two of them). The card asks for more than that, verbatim: "Pin that its return value is unchanged for a row that omits the new field."

A plain truth table would not carry it. A table over (verified_at, blocking) re-derives today's implementation and would stay green if a later edit made the arbiter ALSO require columns_moved_at, because every row would simply be re-derived under the new clause. So each of the four cases is evaluated three times over the same (verified_at, blocking) pair — member absent, member null, member set — and the three verdicts are asserted equal to each other AND to the verdict recorded for the pre-member row. A clause reading the new member breaks that equality whichever way it decides. authorisesIrreversibleAction is pinned the same way because it composes the arbiter, and a source limb asserts the arbiter's body names verified_at and blocking and does not name columns_moved_at at all.

The controls are there so the negatives are not vacuous: the verified row answers true to both predicates, and a row carrying columns_moved_at but no verified_at answers false — a moved column on its own authorises nothing.

The declared pair

sys-migration.object.ts states that the row contract "lives in @objectstack/spec/system (DataMigrationFlagSchema)". That sentence was prose only; nothing held the two halves together. packages/platform-objects/src/system/sys-migration.column-move.pin.test.ts holds them together for this member: the column is a datetime, is readonly, and is required: false — measured, ObjectSchema.create resolves an undeclared required to false rather than leaving it absent, and the pin asserts the resolved value with the three sibling attestation columns as the same-shape control and id / last_run_at as the required: true control. A required column would make "backfilled here, columns not moved" unrepresentable in the ledger, which is the one state mechanism A exists to encode. The pin also asserts the column ORDER of the whole ledger, so a future re-order shows up as a failing test rather than as a wall of false delta in the clause-② instrument.

⛔ No read of the column is pinned. This card adds the declaration and the column and stops; the writer and the reader are the blocked driver card's.

Generated files, and one PM assumption confirmed

The PM's reading was that adding a schema MEMBER adds no EXPORT, so api-surface/system.json and export-origins/system.json should not move while authorable-surface/system.json should. Measured: exactly that.

  • packages/spec/authorable-surface/system.json — one line, system/DataMigrationFlag:columns_moved_at. pnpm --filter @objectstack/spec check:authorable-surface green.
  • pnpm --filter @objectstack/spec check:api-surface and check:export-origins both green with no baseline movement, so nothing exported changed and there is no contention with the parked PR fix(spec,cli): walk a slotted page's slots and a tabs panel's items[].children, and give dashboard global filters a bundle group #16883.
  • content/docs/references/system/migration.mdx — one generated row, string | null, optional. Regenerated with gen:schema && gen:docs on a committed (non-merge) tree; check:docs green afterwards.
  • The four packages/platform-objects/src/apps/translations/* bundle pairs — pnpm check:i18n reported platform-objects DRIFTED (7) before regeneration and in sync (11 bundle(s)) after node scripts/check-i18n-bundles.mjs --write. The three non-default locales receive the English source text, which is the tool's documented merge behaviour and matches every other sys_migration field entry already in those bundles.

Changeset: measured on both halves, not inferred from the path

skip-changeset was tested against what each package actually SHIPS (its files[]), after building, with deviation_detail — the member the previous widening of this same schema added — as the positive control:

published surface columns_moved_at control deviation_detail
packages/spec/dist 6 files 6 files
packages/spec/json-schema 2 files 2 files
packages/spec/src/**/*.zod.ts (shipped verbatim in files[]) 1 file 1 file
packages/spec/api-surface 0 files 0 files
packages/platform-objects/dist 8 files 12 files

Both packages ship it, so this is emphatically not a skip-changeset diff. The dist gap on platform-objects (8 vs 12) is the four translation-bundle files, and it closed once the bundles were regenerated. The changeset is minor on both packages and states what the member ATTESTS — that a migration's column move ran on this deployment, a fact applied_at and verified_at between them cannot express — rather than that a field was added.

Verification

Gate list derived on this tree, not inherited: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, whose change set comes from git with three-dot semantics against the merge base. Everything below was run on the final head 7d2792a7, after origin/main was merged, and every exit code was landed to disk before being read (cmd > log 2>&1; ex=$? — a pipe rewrites the producer's status).

  • 102 derived gate families, 102 run, 102 exit 0. Reconciled: dispatch-gates.mjs --ran reports 102 derived famil(ies) accounted for — 102 run, 0 NOT-MEASURED, 0 UNRUN.
  • The three baseline gates that carry the widening question: check:authorable-surface green with the one added line, and check:api-surface + check:export-origins green with no baseline movement — so nothing exported changed, and there is no contention with the parked PR fix(spec,cli): walk a slotted page's slots and a tabs panel's items[].children, and give dashboard global filters a bundle group #16883 over api-surface/system.json.
  • check:dual-build-cjs-loads needed a full pnpm build first; it exits 3 (PREREQUISITE NOT MET, not a pass and not a red) on an unbuilt tree. The workspace was built and it is green: 104 require entry points across 67 packages load, 620 emitted CJS files parse.
  • pnpm --filter @objectstack/spec test — 468 files / 13118 tests passed. test:repo — 28 files / 407 tests passed.
  • pnpm --filter @objectstack/platform-objects test — 38 files / 550 tests passed.
  • pnpm --filter @objectstack/spec --filter @objectstack/platform-objects typecheck — green, including both packages' check:test-typecheck legs, which are what compile the two new pin files.
  • Two gates were red before being fixed, and both were rerun green: check:docs (the generated content/docs/references/system/migration.mdx had to be regenerated) and check-plugin-teardown-shape.mjs --self-test (red only because its fixture is pinned to a commit this shallow worktree could not reach — green after fetching that object; never a verdict on this diff).
  • Every heavy run went through scripts/pm/os-verify-lock.sh on a stable slot, with verdicts read from the wrapper's own VERDICT command-exit line. One acquisition timed out at exit 99 (queue-timeout, NOT MEASURED) and was resumed on the same slot rather than re-queued.

⚠️ origin/main moved again after this run (a016f08b); the only file it changed that any derived family reads is scripts/engine-double-contract.pinned.json, a roster this diff does not touch — no fake engine is added here. CI decides on the merged head.

验收备注

No out-of-scope findings were filed. Two things noted and deliberately not filed:

  • sys_migration has no schema-to-object parity gate — the "declared pair" is prose plus, as of this PR, a pin for one member. Generalising it is a new instrument and not this card's; noted, not filed. Successor: none identified.
  • The sys_migration field block in zh-CN / ja-JP / es-ES is untranslated English for every member added since created_at, so the new entry joins an existing pattern rather than starting one. Not a defect in the sense of the three filing classes; noted, not filed. Successor: whoever owns the platform-objects translation pass.

Generated by Claude Code


Generated by Claude Code

…oved_at, the nullable datetime attesting the column move

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH
…ns_moved_at and declare the widening

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH
@github-actions github-actions Bot added the size/m label Sep 9, 2026
@github-actions github-actions Bot added documentation Improvements or additions to documentation protocol:system tests tooling labels Sep 9, 2026
@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/platform-objects, @objectstack/spec, touching 6 documentable anchor(s). ⚠️ 1 changed file(s) yielded no anchor (packages/spec/authorable-surface/system.json), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/deployment/seed-tenancy-repair.mdx (via sys_migration (symbol, a field of const object enObjects; a field of const object esESObjects; a field of const object jaJPObjects; a field of const object zhCNObjects))

1 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v17.mdx (via sys_migration (symbol, a field of const object enObjects; a field of const object esESObjects; a field of const object jaJPObjects; a field of const object zhCNObjects))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/spec/authorable-surface/system.json) — pages documenting those are invisible to this run
  • the SDK route bridge reached 60 of 216 client-bound route-ledger rows — the other 156 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 156: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 100 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 131 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json a016f08b8a321dc39f34a384ef3bff0e7b5c1289packageMentionDocs.

Which tree this was computed on

This run read content/docs from 769c3e36c65a63d76c847cce2c1fc4dfd17c8cf9 — the merge of head 7d2792a79db3a0a7fb1ff55e61ac35b6ef87f653 into base a016f08b8a321dc39f34a384ef3bff0e7b5c1289, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 769c3e36c65a63d76c847cce2c1fc4dfd17c8cf9 && git checkout 769c3e36c65a63d76c847cce2c1fc4dfd17c8cf9
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin a016f08b8a321dc39f34a384ef3bff0e7b5c1289 7d2792a79db3a0a7fb1ff55e61ac35b6ef87f653 && git checkout -B drift-repro a016f08b8a321dc39f34a384ef3bff0e7b5c1289 && git merge --no-ff 7d2792a79db3a0a7fb1ff55e61ac35b6ef87f653

node scripts/docs-audit/affected-docs.mjs --json a016f08b8a321dc39f34a384ef3bff0e7b5c1289

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs a016f08b8a321dc39f34a384ef3bff0e7b5c1289 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

os-bill commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator Author

Director seat adoption record — summon #20, session_01Tep4AYXZvyBA7jsvne5KZV (os-bill), 2026-09-09T06:59Z. The verdict below is adopted verbatim from an isolated contract-review subagent (explicit model = CONTRACT_REVIEW_TIER). Transcript tier check before adoption: every harness-stamped model field in the subagent transcript reads claude-fable-5-1 (74 stamps, no other value). Head re-read at posting time = 7d2792a79d, unchanged since the review. ⛔ This seat takes no release action on this carrier (no ready flip, no auto-merge, no enqueue, no label write): the owning seat (domain:spec) adopts this verdict verbatim or discards it, and acts per the state machine.


Contract review (CONTRACT_REVIEW_TIER, isolated seat) — PR #17068 @ 7d2792a79db3a0a7fb1ff55e61ac35b6ef87f653

Verdict: PASS WITH FINDINGS

Increment re-derived as git diff origin/main...refs/pr-review/17068 (merge-base e4fd55d9): 14 files, +344 / −0, a pure insertion. Head grep for columns_moved_at hits exactly those 14 files and nothing else in the tree.

Ruling conformance

Ruling / constraint Reading on this head
#15989 ruling 5556979386 Q1 — mechanism A, field on a separate domain:spec card with its own clause-② review; "do not smuggle into a driver card" This PR is that card. One member (packages/spec/src/system/migration.zod.ts:276-277) + one column (packages/platform-objects/src/system/sys-migration.object.ts:138-148). packages/drivers/**: 0 files.
Card constraint 1 — absence is the contract (optional + nullable, no default) z.string().datetime().nullable().optional(), no .default(). Pinned: data-migration-flag-column-move.pin.test.ts:78-84 asserts 'columns_moved_at' in parsed === false after parse; :86-90 null survives; :191-196 type-level assignability of both spellings.
Constraint 2 — ⛔ do not touch isDataMigrationFlagVerified; pin its verdict is unchanged for a row omitting the field Arbiter body identical base→head (migration.zod.ts:286-289, zero diff lines). Pin :135-158 asserts absent/null/set verdicts equal to each other AND to the pre-member expectation for all four (verified_at, blocking) cases, for both the arbiter and authorisesIrreversibleAction; :172-182 source limb: body names verified_at, blocking, never columns_moved_at; :166-170 a moved column alone authorises nothing.
Constraint 3 — writes nothing, no engine read Verified: no producer, no consumer in the diff (see Derived (b)). Pin file says so explicitly (:22-25).
Constraint 4 — Clause-② expect yes, ≥ minor, changeset says what it attests Re-derived yes; minor on both packages; body states the attestation (see Semver).
Constraint 5 — naming argued, null must read as an expected state columns_moved_at takes the exact SUBJECT_PASTPARTICIPLE_at form of the sibling deviation_observed_at; describe/help text names null as "an expected steady state rather than an error". Triage's added criterion (same describe() style as verified_at/applied_at/deviation_observed_at) met.
Boundaries — no member changes / re-ordering; no docs/adr/**, content/docs/releases/** +344/−0: no existing member moved; member appended after deviation_detail; column inserted before created_at, order pinned (sys-migration.column-move.pin.test.ts:71-87). Governed scan: none.
#16625 ruling A — carrier only with a reviewable increment, both carriers needs:contract-review on PR and card; Clause-②: yes at line start on PR body and in claim 5595422554. This seat ran PM_SWEEP_REPO=objectstack-ai/objectstack node scripts/pm/check-clause2-carriers.mjs --pair 17068exit 0, "both carriers agree".
pm:blocking Unblocks #15989 arm-1 (driver) work — the writer and reader of this member.

Derived judgments

(a) Declaration vs ADR-0104 semantics. docs/adr/0104-field-runtime-value-shape-contract.md on origin/main (2026-09-05 addendum): the attestation key is the sys_migration row adr-0104-file-references (L1125-1135), written by os migrate files-to-references --applyrunFilesToReferencesMigrationrecordDataMigrationRun, or by attestFreshDatastore at birth; step 3b (L1157-1160) is "unquote every cell, retype the column to the string column"; population 2 (L1245-1255) requires the step's completion be "one answer, pinned, and it must fail toward the JSON arm". The describe — "retypes the migrated columns and rewrites the values they hold into the new encoding … Null/absent … is what a consumer that cannot read this field must assume" — matches 3b and the fail-toward-JSON requirement exactly, and the nullable/optional datetime is the ruling's "failure mode is absence". Shape identical to the three sibling attestations (:263, :265, :272).

(b) Producer / consumer — declared = enforced. In the diff: none exists, by the card's own constraint 3. Outside the diff, on this head: writers recordDataMigrationRun (packages/platform-objects/src/system/migration-flag.ts:173-206, row built from a fixed literal — no columns_moved_at), attestFreshDatastore (:310), the engine admit path (packages/objectql/src/engine.ts:8136-8145, deviation fields only); reader readDataMigrationFlag (migration-flag.ts:62-96) projects an explicit column list and therefore drops columns_moved_at — on this head no DataMigrationFlag value produced by the repo's own row reader can carry the member; the engine (engine.ts:7917) reads through that reader into the arbiter only. All within scope; recorded as F1/F2 because they are the exact seams the driver card must open.

(c) Stored-row impact. Additive nullable column; syncSchema is additive and never retypes (ADR L1281-1285) → created on next sync, no data rewrite, no conversion. ADR-0087: not owed — nothing previously accepted is refused; check-adr-0087-registration.mjs fires only on a declared-breaking changeset. Liveness ledger: not owed — scope is BUILTIN_METADATA_TYPE_SCHEMAS (packages/spec/liveness/README.md:27-30); no system/migration ledger file among the 38; the schema's siblings appear in none; CI Spec property liveness green. Baselines (regenerated, not hand-edited): authorable-surface/system.json +1 at the alphabetical slot the generator emits; content/docs/references/system/migration.mdx +1 row in schema order; 4× *.objects.generated.ts (+4, identical English text, the documented merge behaviour) and 3× *.source-hashes.generated.ts (+2). Correctly unmoved: api-surface/system.json, export-origins/system.json, declaration-map/system.json, json-schema.manifest/system.json — all schema-level rosters carrying no members; authorable-surface.base.json is the pinned deletion anchor and must not move; packages/spec/json-schema/ is gitignored build output (.gitignore:63). Coverage instrument: every tracked file on origin/main naming the sibling deviation_detail that is a generated artifact is in the diff; the untouched ones (migration-flag.ts, engine.ts, seed-tenancy-backfill.ts, migrate-sys-notification-to-event.ts, tests, CHANGELOG.mds) enumerate no full column set or are release-owned.

(d) New exports / error codes / other keys. Exports: none (api-surface records existence only; nothing exported added). Error codes: none. Other payload keys: the paired column sys_migration.columns_moved_at (Field.datetime, readonly: true, resolved required: false, pinned sys-migration.column-move.pin.test.ts:36-57).

Semver / changeset

Boundary flags

Findings

  • F1 (non-blocking)packages/platform-objects/src/system/migration-flag.ts:62-96: readDataMigrationFlag projects a fixed column list; the declared member is dropped on read. In scope per constraint 3 and explicitly un-pinned (data-migration-flag-column-move.pin.test.ts:22-25), but the driver card must extend this projection or the member never reaches its predicate. No change requested here.
  • F2 (non-blocking)migration-flag.ts:173-206: recordDataMigrationRun rebuilds the row from a literal and its update path sets only listed keys, so a re-record after step 4 neither sets nor clears columns_moved_at; a later failing run nulls verified_at but leaves the column attested — physically correct, and the ruling's "verified AND non-null" composition still fails closed. The driver card should pin this explicitly. Informational.
  • F3 (non-blocking) — ADR-0104 on origin/main still sketches step 4 as { id, verified_at, blocking: 0 } (L1162) and names no attestation member. Governed, so a docs-only successor; not filed by the dev's out-of-scope list. Note for the PM.
  • F4 (non-blocking, cosmetic) — the describe/help text's "Null … still store the legacy encoding" is meaningful only for a migration that has a column move; the same sentence is emitted into every sys_migration row's help via the i18n bundles (en.objects.generated.ts +4). "for this migration" scopes it; acceptable.
  • F5 (non-blocking, cosmetic) — the PR body now ends with two _Generated by …_ footers (session-URL form, then the bare platform form), contradicting the dev report's "exactly 1 footer" read-back — the seat's later body rewrite appears to have appended the second. Not a contract matter.

Dev report open_questions: Q1 (C1 exit 4 on Part of #15989) → A is right and is already moot: the seat rewrote the line, this seat re-ran --pair 17068 → exit 0; hanging a carrier on #15989 would assert a review that does not exist there. Q2 (footer form) → A; see F5; not a contract concern. deviations: attribution trailers — outside contract scope; changeset measured on files[] — confirmed against packages/spec/package.json; generated files regenerated — shapes consistent with their generators; origin/main drift — re-measured, none on PR paths.

CI at read time

44 check runs on 7d2792a734 latest-per-name: 30 success, 4 skipped, 0 failure, 0 in-progress. Skipped: Auto Label, Check PR Size, Console Pin Gate, Packed-tarball smoke (opt-in) — all conditional/opt-in. Green and load-bearing here: Check Changeset, Lint & Repo Gates, Spec property liveness, Governed Surface Queue Guard, Part-of PR must not also close its card, both "No other open PR may claim…" gates, Type Check ×5, Test Core ×7, Temporal Conformance (live PG + MySQL), Build Core, Build Docs, Dogfood ×5. Computed on merge 769c3e36 (head into a016f08b).

Implemented-by: branch claude/issue-16185-migration-flag-column-move-attest
Reviewed-by: director seat summon #20 (isolated fable subagent, transcript-verified before adoption)

{"pr":17068,"head":"7d2792a79db3a0a7fb1ff55e61ac35b6ef87f653","verdict":"PASS WITH FINDINGS","blocking":[],"clause2":"yes","semver_ok":true,"governed":false,"ci":"34 latest-per-name: 30 success, 4 skipped (conditional/opt-in), 0 failed, 0 pending"}


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:system size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

spec(system): DataMigrationFlagSchema gains the nullable datetime that attests ADR-0104 step 3 ran on this deployment

2 participants